
----------------------------------------------------------------
--- Copyright：  迷你玩-海马
--- Description：mysql封装 跟具体业务无关
----------------------------------------------------------------

local assert     = assert
local require    = require    
local bit        = bit
local coroutine  = coroutine
local debug      = debug
local io         = io
local pairs      = pairs
local ipairs     = ipairs
local math       = math
local os         = os
local print      = print
local pcall      = pcall
local xpcall     = xpcall
local rawget     = rawget
local rawset     = rawset
local select     = select
local string     = string
local table      = table
local tonumber   = tonumber
local tostring	 = tostring
local error      = error
local type       = type
local unpack     = unpack
local setmetatable = setmetatable
local getmetatable = getmetatable
local ngx = ngx

local log       = require("modules.commhm.log");
local ns_time   = require("modules.commhm.time");
local uu        = require("modules.commhm.utils");

local ns_mysql  = require("resty.mysql");


-- MYSQL MGR类
local _M = { _VERSION = '0.12.1' }
local mt = { __index = _M }


function _M.new(self, pre_ , config_server_, config_select_ )
	log.debug( log.fast() and "call mysqlMgr:new" );
	local ins_ = {
		config_cache  = {};
		pre           = pre_;
		config_server = config_server_;
		config_select = config_select_;

		_cc        = 0;   --计数
		_last_time = uu.now();
	};
    return setmetatable(ins_, mt);
end


_M.init_pool = function(self)
end


_M.final = function(self)
end


--insert { insert_id=0, server_status=2, warning_count=0, affected_rows=1, }
--update { insert_id=0, server_status=2, warning_count=0, affected_rows=2, }
--select {
--  [1] = {  star=3, create_time=1488427427  }
--  [2] = {  star=3, create_time=1488427635  }
--}
-- select_ = database name;


-- 请求一个mysql语句
_M.query = function(self, sql, select_ )
	local now_ = uu.now()
	if  now_ - self._last_time > 60 then
		if  ngx.worker.id() == 0 or self._cc >= 60*10 then
			log.day_list( "mysql", "query", select_, ngx.worker.id(), self._cc,
					now_ - self._last_time, math.floor(self._cc/(now_-self._last_time)) )
		end

		self._last_time = now_
		self._cc = 0
	end
	
	self._cc = self._cc + 1
    local instance = self:try_connect(select_ );
    local res, err, errcode, sqlstate = instance:query(sql);

    if  not res then
		log.day_list( "mysql", "error", select_, err, errcode, sqlstate, sql );
		self:error_exit( "mysql query error:" .. (err or "nil") .. ", " .. (errcode or "nil")
				.. ", "	.. (sqlstate or "nil") .. "\n" .. sql );
		return false, {};
	else
		self:close()  --正常关闭
		log.debug( log.fast() and "call mysql query ok=true" );
		uu.var_dump( res );
		return true, res;
    end
end



--mysql批量执行，可一次执行多条语句
--参数：self 自身table
--     sql_ sql语句，多条语句以;隔开
--     connect_key_，对应myslq配置中的数据库名，用以指定是哪个数据库
--返回值：1是否成功
--       2数据库执行结果
_M.query_mult = function (self, sql, select_)
	local now_ = uu.now()
	if  now_ - self._last_time > 60 then
		if  ngx.worker.id() == 0 or self._cc >= 60*10 then
			log.day_list( "mysql", "query", select_, ngx.worker.id(), self._cc,
					now_ - self._last_time, math.floor(self._cc/(now_-self._last_time)) )
		end

		self._last_time = now_
		self._cc = 0
	end
	
	self._cc = self._cc + 1
	local allRet = {}
	local instance = self:try_connect(select_)
	
    local res, err, errcode, sqlstate = instance:query(sql)
    if  not res then
		log.day_list( "multi_query_error", "mysql", select_, err, errcode, sqlstate, sql );
		self:error_exit("mysql multi query error:" .. (err or "nil") .. ", " .. (errcode or "nil")
				.. ", " .. (sqlstate or "nil") .. "\n" .. sql)
		return false, {}
	end
    table.insert(allRet, res)
	local i = 2
	while err == "again" do
        res, err, errcode, sqlstate = instance:read_result()
        if  not res then
			log.day_list( "multi_query_error", "mysql", i, select_, err, errcode, sqlstate, sql );
			self:error_exit("mysql multi query error:" .. i .. "|" .. (err or "nil")
					.. ", " .. (errcode or "nil") .. ", " .. (sqlstate or "nil") .. "\n" .. sql)
			return false, {}
        end
		table.insert(allRet, res)
        i = i + 1
	end

	self:close()   --正常关闭
	log.debug(log.fast() and "call mysql query ok=true")
	uu.var_dump(res)

	return true, allRet
end



-------------------------------------------------------

_M.error_exit = function(self, txt)
	log.error( txt );
	self:close(true);
	if  ngx.ctx.err_call_back then
		ngx.ctx.err_call_back(txt);
	end
end


_M.close = function( self, shutdown )
	if  ngx.ctx.last_mysql_instance then
		self:private_close( ngx.ctx.last_mysql_instance, shutdown )
		ngx.ctx.last_mysql_instance = nil
	end
end


_M.private_close = function( self, instance, shutdown )
	local ok, err;
	if  shutdown then
		ok, err = instance:close();
		log.day( "error", "mysql|" .. self.pre .. "|close" );
	else
		ok, err = instance:set_keepalive( 120000, 16 );
		--log.day( "mysql_connect" , "close|" .. select_ .. "|" .. ngx.worker.pid() );
	end

	if  not ok then
    	log.error( "failed to set mysql set_keepalive: " .. (err or "nil") .. ", pre=" .. self.pre )
    else
    	log.debug( log.fast() and "mysql connection keep alive ok. pre=" .. self.pre );
    end
end


---------------------------

-- 按照 config 分表获得连接IP和端口
_M.get_config_by_key = function(self, select_)
	log.debug( log.fast() and "get_config_by_key, select=" .. select_ );

	if  self.config_cache[ select_ ]  then
		return  self.config_cache[ select_ ];
	else
		--log.debug( "new mysql config" );
		if  self.config_server and self.config_select then
			if  self.config_select[select_] then
				local ret_ = self.config_server[ self.config_select[select_] ];
				if  ret_ then
					log.debug( log.fast() and "set config_cache for " .. select_ );
					self.config_cache[ select_ ] = ret_;
					return ret_;
				end
			end
		end
	end

	self:error_exit( 'config mysql select, error = ' .. (select_ or 'nil') );
end


--获得连接
_M.try_connect = function( self, select_ )
	--self:init_pool();
	local config_ = self:get_config_by_key( select_ );
	local instance = self:private_try_connect( config_, select_ )
	ngx.ctx.last_mysql_instance = instance
	return instance
end


-----------------------------------------------------------------------------utils---------------------
-- config = { ip, port, user, pass, select }
_M.private_try_connect = function( self, config_, select_ )
	local instance = ns_mysql:new()
	if  instance.set_timeouts then
		instance:set_timeouts(3000, 3000, 3000);
	else
		instance:set_timeout(3000);      --3s
	end

	--log.debug( log.fast() and "set pool name=" .. config_.pool );

	local ok, err, errcode, sqlstate = instance:connect( config_ );
	if not ok then
		log.day_list( "mysql", "error", select_, err, errcode, sqlstate, "connect_fail" );
		self:error_exit( "fail connect. err=" .. (err or "nil") .. ", errcode=" .. (errcode or "nil")
				.. ", sqlstate=" .. (sqlstate or "nil")  );
	end

	local count, err = instance:get_reused_times();
	if  err then
		log.day_list( "mysql", "error", select_, count, err, "get_reused_times" );
		self:error_exit( "fail mysql get_reused_times. msg=" .. err );
	else
		log.debug( log.fast() and "mysql get_reused_times=" .. count );
	end

	return instance;
end


return _M;
